package org.zhs.linear;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;

/**
 * @Author: Administrator
 * @Date: 2024/3/27 22:25
 * @Version: 1.0
 * @Description:
 */
public class MyArrayList<E> {

    /**
     * 默认容量大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 当前包含的容量
     */
    private int theSize;

    /**
     *
     */
    private Object[] theItems;

    public MyArrayList() {
        //调用doClear方法完成初始化
        doClear();
    }

    /**
     * 用于把ArrayList恢复到初始状态
     */
    private void doClear() {
        theSize = 0;
        ensureCapacity(DEFAULT_CAPACITY);
    }

    /**
     * 返回当前长度
     */
    public int size() {
        return theSize;
    }

    /**
     * 判断当前ArrayList是否是空
     */
    public boolean isEmpty() {
        return theSize == 0;
    }

    /**
     * 获取指定元素
     *
     * @param index 下标
     * @return
     */
    public E get(int index) {
        if (index < 0 || index >= theSize) {
            throw new ArrayIndexOutOfBoundsException();
        }
        return (E) theItems[index];
    }


    /**
     * 向对应下标位置设置值
     *
     * @param index
     * @param newElement
     */
    public E set(int index, E newElement) {
        if (index < 0 || index >= theSize) {
            throw new ArrayIndexOutOfBoundsException();
        }
        Object oldElement = theItems[index];
        theItems[index] = newElement;
        return (E) oldElement;
    }

    /**
     * 该函数用于ArrayList的初始化与扩充
     * 确保ArrayList的 {@link #theItems} 能进行初始化使其拥有默认容量大小
     *
     * @param newCapacity 新的容量大小
     */
    public void ensureCapacity(int newCapacity) {
        //如果新的容量小于当前容量的话，直接return 无需扩充或初始化
        if (newCapacity < theSize) {
            return;
        }

        //创建临时的变量，用于保存之前的数组
        Object[] old = theItems;
        //对数组进行扩充
        theItems = new Object[newCapacity];

        if(old == null){
            return;
        }
        //对临时变量进行遍历，把之前的数据保存到 theItems 中
        for (int i = 0; i < old.length; i++) {
            theItems[i] = old[i];
        }
    }


    /**
     * 添加元素
     *
     * @param element
     * @return
     */
    public boolean add(E element) {
        //如果当前数组容量，等于当前数组中元素的长度,则需要对数组进行扩容
        if (theItems.length == size()) {
            //扩充原数组长度的两倍 + 1
            ensureCapacity(size() * 2 + 1);
        }
        theItems[theSize++] = element;
        return true;
    }

    /**
     * 移除指定位置的元素
     *
     * @param index
     * @return
     */
    public E remove(int index) {
        Object old = theItems[index];

        //移除指定位置的元素，实际就是把指定位置之后的元素都向前挪动
        for (int i = index; i < size(); i++) {
            theItems[i] = theItems[i + 1];
        }

        //元素长度-1
        theSize --;
        return (E)old;
    }


    /**
     * 不实现 Iterator 自己实现foreach
     * 主要就是使用函数式接口 {@link Consumer}
     * {@link Consumer} 提供了两个函数，主要使用 {@link Consumer#accept(Object)} 方法，本质是自己实现一个匿名内部类
     *
     * @param consumer
     */
    public void foreach(Consumer<E> consumer){
        //此处遍历到实际的容量大小
        for (int i = 0; i < theSize; i++) {
            consumer.accept( (E)theItems[i] );
        }
    }

    public Iterator<E> iterator(){
        return new Itr();
    }

    /**
     * 此处如果要现实ArrayList的迭代器功能，需要实现{@link Iterator}接口
     * 并且查看ArrayList的源码，它是自己定义了一个内部类，使用该内部类进行实现 Iterator接口，用来实现
     */
    private class Itr implements Iterator<E>{

        /**
         * 要返回的下一个元素的下标，
         * 在这里默认值为0，因为数组的下标从0开始
         * *********但是请注意一点，查看{@link java.util.ArrayList.Itr } 的源码，会发现它的cursor并没有赋值
         * 实际上它的赋值操作是由JVM保证的，所以我们实现的时候需要自己手动指定
         */
        private int cursor = 0 ;

        /**
         * 这个值是用来记录上次返回的元素的下标
         */
        private int lastRet = -1;

        /**
         * 判断是否有一个元素，其实就是当前元素的下标是否等于 实际容量大小
         * @return
         */
        @Override
        public boolean hasNext() {
            return cursor != theSize;
        }

        @Override
        public E next() {
            //如果当前的游标大于等于实际容量的时候
            int i = cursor;

            if (i >= theSize)
                throw new NoSuchElementException();

            //对游标加1，下次next就会获取到下一个元素
            cursor = i + 1;

            return (E)theItems[lastRet = i];
        }

        @Override
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();

            // 当要删除当前被遍历的元素时，需要把已经自增了的 cursor 重新改成被删除的元素的下标
            cursor = lastRet;

            // 移除实际上就是把后面的元素往前移动一位
            // 调用外部类的remove方法，因为迭代器移除本质也是的外部类中的数据
            // 传入的 lastRet ， 本质就是当前 元素因为游标已经下移，如果使用 cursor就是删除的下一个元素。
            // 所以需要用lastRet记录当前正在被遍历的元素的下标
            MyArrayList.this.remove(lastRet);
        }
    }

}
